package jot.jot;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * java类生成xml文件
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-11-01 14:09
 */
public  class ClassGenerateXml {

    //一个Java类只有一个公共类，如果一个java文件存在多个公共类，则抛出异常
    private static boolean clazzFlag = false;

    public static void main(String[] args) throws IOException {
        //文件名
        String jsonName = "java_generate.java";
        //路径
        String path = "D:\\JAVA\\poi\\java_generate_xml";
        //读取文件内容
        String xml = read(path, jsonName);
        //开始初始化和创建
        build(xml,path,jsonName.substring(0,jsonName.lastIndexOf(".")));
    }

    public static void build(String xml, String path, String fileName) throws IOException {
        StringBuilder content = new StringBuilder();

        //还没有找到package
        boolean isPackage = false;
        //还没有全部找到import
        boolean isImport = false;

        int i = 0;

        //找到package（包）
        //找package 关键字
        int index = indexOf(xml,i,"package ","没有找到package 关键字");
        //找package 后面的;
        int lastIndex= indexOf(xml,index+("package ".length()),";","没有找到package 的结束字符;");
        //将下标移到;+1
        i = lastIndex + 1;
        //已经读取到package了
        isPackage = true;
        content.append("<package value=\""+xml.substring(index+("package ".length()),lastIndex)+"\"/>\r\n\r\n");

        //找到class
        //sb 存储了类的信息

        while(i<xml.length()){
            //找到所有的import （导包
            while(!isImport){
                StringBuilder clazz = new StringBuilder();
                int temp = findFieldAfter(xml, i, clazz,";")+1;
//                System.out.println("import == "+clazz.toString());

                if(clazz.toString().indexOf("import")!=-1){
                    String[] importField = clazz.toString().split(" ");
                    if(importField.length>1){
                        content.append("<import value=\""+importField[importField.length-1]+"\"/>\r\n");
                        i = temp;
                    }else{
                        throw new RuntimeException("导包异常;");
                    }

                }else{
                    isImport = true;
                    break;
                }
            }

            //注解集合
            List<String> annotations = new ArrayList<>();
            //找类的注解
            int annotationIndex = -1;
            while ((annotationIndex = findFieldAfter(xml, i, null, "@","p","f","c",";"))!=-1){
                //找到后，确定是否是注解
                if(annotationIndex<xml.length()&&xml.charAt(annotationIndex)=='@'){
                    //开始扫描注解  向前扫
                    int annotationEndIndex = findEndMethod(xml, annotationIndex + 1, xml.length(), '(', ')',' ','\r','\n');
                    String annotation = xml.substring(annotationIndex,annotationEndIndex+1);
                    System.out.println("注解 === "+annotation);
                    annotations.add(annotation);
                    i = annotationEndIndex+1;
                }else{
                    break;
                }
            }
            System.out.println("@ ==> "+annotations.size());

            //找类
            StringBuilder sb = new StringBuilder();
            int classIndex = getClazzIndex(xml,i,sb);
            if(classIndex==-1){
                throw new RuntimeException("没有找到类！");
            }

            //拼类属性
            content.append(sb);

            //拼类名
            StringBuilder className = new StringBuilder();
            i = findFieldAfter(xml, classIndex, className," ");
            content.append("name=\""+className+"\">\r\n");

            //插入注解
            for(String annotation : annotations){
                addAnnotation(annotation,content);
            }

            //找到类的括号
            int classStart = indexOf(xml, i, xml.length(), "{");
            if(classStart==-1){
                throw new RuntimeException("类中确实了{}括号");
            }
            //查找是否有继承的 父类
            int extendsIndex = indexOf(xml, i, classStart, "extends ");
            if(extendsIndex!=-1){
                StringBuilder extendsName = new StringBuilder();
                i = findFieldAfter(xml,extendsIndex+("extends ".length()),extendsName," ","{");
                content.append("\t<extends name=\""+extendsName+"\"/>\r\n");
            }
            //查找是否有实现接口
            int implementsIndex = indexOf(xml, i, classStart, "implements ");
            if(implementsIndex!=-1){
                StringBuilder implementsName = new StringBuilder();
                i = findFieldAfter(xml,implementsIndex+("implements ".length()),implementsName," ","{");

                content.append("\t<implements name=\""+implementsName+"\"/>\r\n");
            }


            //开始遍历类内部内容
            i = classStart+1;
            //1方法、2属性、0未知
            int isMethod = 0;
            StringBuilder clazzSB = new StringBuilder();
            boolean isException = false;
            while(i<xml.length()){
                //找下一个字段
                if(isMethod==1){
                    //方法
                    StringBuilder clazz = new StringBuilder();
                    i = findFieldAfter(xml, i, clazz, " ","(",",",")","{",";","}");
                    if(clazz.toString().isEmpty()&&i==xml.length()-1){
                        break;
                    }

                    if(!clazz.toString().trim().isEmpty()){
                        if(clazz.toString().equals("final")){
                            clazzSB.append(" isfinal=\"true\" ");
                        }else if(!isException&&(
                                clazz.toString().equals("short")||
                                        clazz.toString().equals("int")||
                                        clazz.toString().equals("long")||
                                        clazz.toString().equals("byte")||
                                        clazz.toString().equals("float")||
                                        clazz.toString().equals("double")||
                                        clazz.toString().equals("boolean")||
                                        clazz.toString().equals("char")||
                                        (clazz.toString().charAt(0)>=65&&clazz.toString().charAt(0)<=90))){
                            clazzSB.append(" type=\""+clazz+"\"");
                        }else if(clazz.toString().equals("throws")){
                            isException = true;
                            continue;
                        }else{
                            clazzSB.append(" name=\""+clazz+"\"");
                        }
                    }

                    //必须时空串，才能执行这一段，因不是空串说明有值需要添加
                    if(clazz.toString().trim().isEmpty()||xml.charAt(i)==','||xml.charAt(i)==')'||xml.charAt(i)=='{'){
                        //区分参数和异常
                        if(clazzSB.toString().indexOf("type")!=-1){
                            content.append("\t\t<arg "+clazzSB+"/>\r\n");
                        }else if(!clazzSB.toString().isEmpty()){
                            content.append("\t\t<exception "+clazzSB+"/>\r\n");
                        }
                        clazzSB = new StringBuilder();
                        if(xml.charAt(i)=='{'){
                            //找到方法中的  }结束符

                            int temp = findEndMethod(xml,i,xml.length(),'{','}');
                            String code = xml.substring(i+1, temp);
                            content.append("\t\t<code>"+code+"\r\n\t\t</code>\r\n\t</method>\r\n\r\n");
                            i = temp+1;
                            isException = false;
                            isMethod = 0;
                        }
                        i++;
                        continue;
                    }

                }else if(isMethod==2){
                    //属性
                    if(xml.charAt(i-1)==';'){
                        isMethod = 0;
                        content.append("\t<attribute "+clazzSB.toString()+"\"/>\r\n");
                        clazzSB = new StringBuilder();
                    }else{
                        StringBuilder clazz = new StringBuilder();
                        i = findFieldAfter(xml, i, clazz, " ",";");//比如找到了 private
                        if(clazz.toString().isEmpty()&&i==xml.length()-1){
                            break;
                        }
                        if(clazz.toString().equals("new")){
                            clazzSB.append(clazz+" ");
                        }else{
                            clazzSB.append(clazz);
                        }
                    }
                }else if(isMethod==0){
                    //未知
                    StringBuilder clazz = new StringBuilder();
                    i = findFieldAfter(xml, i, clazz, " ","(",",","{",";","}");//比如找到了 private


                    if(clazzSB.toString().isEmpty()&&clazz.toString().isEmpty()&&xml.charAt(i)=='}'){
                        //类标签结束
                        content.append("\r\n</class>\r\n\r\n");
                        i++;

                        int temp = findFieldAfter(xml,i,clazz," ");
                        if(temp==xml.length()||clazz.toString().isEmpty()){
                            //类结束！
                            i = xml.length();
                        }
                        break;
                    }


                    if(clazzSB.toString().isEmpty()&&clazz.toString().isEmpty()&&xml.charAt(i)=='{'){
                        //代码块
                        int temp = findEndMethod(xml,i,xml.length(),'{','}');
                        String code = xml.substring(i+1, temp);
                        content.append("\t<code>"+code+"\t</code>\r\n");
                        i = temp + 1;
                        continue;
                    }
                    if( ((clazzSB.toString().isEmpty()&&clazz.toString().equals("static"))||
                            (clazzSB.toString().indexOf("is-static")!=-1&&clazz.toString().isEmpty()))
                            &&xml.charAt(i)=='{'){
                        //静态代码块
                        int temp = findEndMethod(xml,i,xml.length(),'{','}');
                        String code = xml.substring(i+1, temp);
                        content.append("\t<static>"+code+"\t</static>\r\n");
                        if(!clazzSB.toString().isEmpty()){
                            clazzSB = new StringBuilder();
                        }
                        i = temp + 1;
                        continue;
                    }

                    System.out.println("feild = "+clazz.toString());
                    if(clazz.toString().equals("public")||clazz.toString().equals("private")||clazz.toString().equals("protected")){
                        clazzSB.append(" scope=\""+clazz+"\"");
                    }else if(clazz.toString().equals("static")){
                        clazzSB.append(" is-static=\"true\" ");
                    }else if(clazz.toString().equals("final")){
                        clazzSB.append(" is-final=\"true\" ");
                    }else if(clazz.toString().equals("abstract")){
                        //抽象方法
                        isMethod = 1;
                        clazzSB.append(" is-abstract=\"true\" ");
                    } else if(clazz.toString().equals("short")||clazz.toString().equals("int")||clazz.toString().equals("long")||clazz.toString().equals("byte")||clazz.toString().equals("void")||
                            clazz.toString().equals("float")||clazz.toString().equals("double")||clazz.toString().equals("boolean")||clazz.toString().equals("char")||(clazz.toString().charAt(0)>=65&&clazz.toString().charAt(0)<=90)){
                        clazzSB.append(" type=\""+clazz+"\"");
                    }else{
                        if(clazz.toString().indexOf("=")!=-1){
                            //属性名包含=
                            String[] splitField = clazz.toString().split("=");
                            //属性名或方法名
                            clazzSB.append(" name=\""+splitField[0]+"\"");
                            if(splitField.length>1){
                                if(splitField[1].equals("new")){
                                    clazzSB.append("value=\""+splitField[1]+" ");
                                }else{
                                    clazzSB.append("value=\""+splitField[1]);
                                }
                                //不存在分号，还未结束
                                //属性
                            }
                            isMethod = 2;
                        }else{
                            //属性名不包含=
                            clazzSB.append(" name=\""+clazz+"\" ");
                            //下一个字段则可以判断出属性还是方法
                            clazz = new StringBuilder();
                            i = findFieldAfter(xml, i, clazz, " ",";",")");
                            if(clazz.toString().isEmpty()&&i==xml.length()-1){
                                break;
                            }
                            if(clazz.toString().indexOf("=")!=-1){
                                //属性
                                clazzSB.append(" value=\"");
                                isMethod = 2;
                            }else{
                                //方法
                                clazzSB.append(">\r\n");
                                content.append("\t<method "+clazzSB);
                                clazzSB = new StringBuilder();
                                isMethod = 1;

                                if(clazz.toString().length()<=1){
                                    continue;
                                }
                                clazz = new StringBuilder(clazz.substring(1));
                                if(clazz.toString().equals("final")){
                                    clazzSB.append(" isfinal=\"true\" ");
                                }else if(clazz.toString().equals("short")||clazz.toString().equals("int")||clazz.toString().equals("long")||clazz.toString().equals("byte")||
                                        clazz.toString().equals("float")||clazz.toString().equals("double")||clazz.toString().equals("boolean")||clazz.toString().equals("char")||(clazz.toString().charAt(0)>=65&&clazz.toString().charAt(0)<=90)){
                                    clazzSB.append(" type=\""+clazz+"\"");
                                }
                            }
                        }
                    }
                }
                i++;
            }
        }


        //找到所有方法
        write(path,fileName+".xml",content.toString());

    }

    /**
     * 从方法的{下标+1开始找，找到该方法的}下标，结束方法
     * @param xml
     * @param i
     * @param j
     * @return
     */
    public static int findEndMethod(String xml,int i ,int j,char begin,char end,char... args){
        int left = 0;
        for(;i<j;i++){
            if(xml.charAt(i)=='\''){
                int temp = i + 1;
                while (temp<j&&(temp = indexOf(xml,temp,j,"\'"))!=-1){
                    if(xml.charAt(temp-1)=='\\'){
                        if(xml.charAt(temp-2)=='\\'){
                            break;
                        }
                    }else{
                        break;
                    }
                    temp++;
                }

                if(temp==-1){
                    throw new RuntimeException("没有匹配到对应的单引号！");
                }
                i = temp + 1;
            }
            if(xml.charAt(i)=='"'){
                int temp = i + 1;
                while (temp<j&&(temp = indexOf(xml,temp,j,"\""))!=-1){
                    if(xml.charAt(temp-1)=='\\'){
                        if(xml.charAt(temp-2)=='\\'){
                            break;
                        }
                    }else{
                        break;
                    }
                    temp++;
                }
                if(temp==-1){
                    throw new RuntimeException("没有匹配到对应的双引号！");
                }
                i = temp + 1;
            }
            //如果找到空格，并且还未找到()括号，说明这个注解没有括号
            if(args!=null&&args.length>=1){
                for(char ch : args){
                    if(xml.charAt(i)==ch&&left==0){
                        return i-1;
                    }
                }

            }
            if(xml.charAt(i)==begin){
                left++;
            }
            if(xml.charAt(i)==end){
                left--;
                if(left==0){
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 找到字段的下一个字段
     * @param xml
     * @param prevFieldIndex
     * @param content
     * @param args
     * @return
     */
    public static int findFieldAfter(String xml,int prevFieldIndex,StringBuilder content,String...args){
        //开始查找字段
        int fieldBegin = prevFieldIndex;
        //找到字段的第一个下标
        boolean flag = true;
        while(flag){
            flag = false;
            while(fieldBegin<xml.length()&&(xml.charAt(fieldBegin)==' '||xml.charAt(fieldBegin)=='\r'||xml.charAt(fieldBegin)=='\n')){
                fieldBegin++;
            }
            if(fieldBegin==xml.length()){
                return xml.length()-1;
            }
            //找到//的话，直接找到\r\n再继续读取
            if(xml.charAt(fieldBegin)=='/'&&fieldBegin+1<xml.length()&&xml.charAt(fieldBegin+1)=='/'){
                fieldBegin = find('\r','\n',xml,fieldBegin);
                if(fieldBegin==-1){
                    System.out.println("找到//注释，但没有找到结束换行标识，说明//是最后一行");
                    fieldBegin = xml.length();
                    break;
                }
                fieldBegin++;
                flag = true;
            }
            //扎到/*的话，直接找到*/再继续读取
            if(xml.charAt(fieldBegin)=='/'&&fieldBegin+1<xml.length()&&xml.charAt(fieldBegin+1)=='*') {
                fieldBegin = find('*','/',xml,fieldBegin);
                if(fieldBegin==-1){
                    throw new RuntimeException("只找到了/*注释，但没有找到结束标识：*/");
                }
                fieldBegin+=1;
                flag = true;
            }
        }

        //找到类名后的第一个空格下标
        int fieldAfter = Integer.MAX_VALUE;
        for(String arg:args){
            int temp = indexOf(xml, fieldBegin, xml.length(), arg);
            if(temp>=0&&temp<fieldAfter){
                fieldAfter = temp;
            }
        }
        if(fieldAfter==Integer.MAX_VALUE){
            fieldAfter = xml.length();
        }

        //得到 类名
        if(content!=null){
            content.append(xml.substring(fieldBegin, fieldAfter));
        }
        return fieldAfter;
    }

    /**
     * 写入注解
     * @param annotation
     * @param content
     */
    public static void addAnnotation(String annotation ,StringBuilder content){
        int args = indexOf(annotation, 0, annotation.length(), "(");

        if(args==-1){
            //注解不存在参数，比如  @Service
            content.append("\t<annotation name=\""+annotation.substring(1)+"\"/>\r\n");
        }else{
            //追外层只有一个注解
            content.append("\t<annotation name=\""+annotation.substring(1,args)+"\">\r\n");
            //查找注解中的参数
            int annotationEnd = findEndMethod(annotation, args, annotation.length(), '(', ')');
            String body = annotation.substring(args + 1, annotationEnd);
            //参数插入
            content.append("\t\t<arg>\r\n");
            if(body.indexOf("@")!=-1){
                //一下是参数是否有注解
                int temp = indexOf(body,0,body.length(),",");
                if(temp!=-1){
                    //注解里有多个注解，分别用逗号隔开
                    List<String> annotations = new ArrayList<>();
                    annotations.add(body.substring(0,temp));
                    int tempEnd = -1;
                    while((tempEnd = indexOf(body,temp+1,body.length(),","))!=-1){
                        annotations.add(body.substring(temp+1,tempEnd));
                        temp = tempEnd;
                    }

                    annotations.add(body.substring(temp+1,body.length()));

                    for(String anno : annotations){
                        System.out.println("anno = "+anno);
                        addAnnotation(anno,content);
                    }
                }else{
                    //注解里只有一个注解
                    addAnnotation(body,content);
                }
            }else{
                //注解里没有注解，直接添加
                content.append(body+"\r\n");
            }
            content.append("\t\t</arg>\r\n");
            //注解结束
            content.append("\t</annotation>\r\n");

        }
    }

    /**
     * 找到类的索引
     * @param xml
     * @param i
     * @param sb
     * @return
     */
    public static int getClazzIndex(String xml,int i,StringBuilder sb){
        int index = indexOf(xml, i ,xml.length(), "class ");
        int lastIndex = index+("class ".length());
        boolean isFinal = false;
        boolean isAbstract = false;
        if(index!=-1){
            sb.append("<class ");
            String modifier = getModifier(xml, index);
            if(modifier.equals("true")){
                //找到类，但没有任何修饰符
                return lastIndex;
            }else if(modifier!=null){
                //找到后，进行拆分关键字
                String[] split = modifier.split(":");
                while (!clazzFlag){
                    if(split[0].equals("public")){
                        if(clazzFlag){
                            throw new RuntimeException("一个Java类存在多个公共类");
                        }
                        clazzFlag = true;
                        sb.append(" scope=\"public\" ");
                        return lastIndex;
                    }else if(split[0].equals("abstract")){
                        if(isFinal){
                            throw new RuntimeException("abstract 和 final 不能共存，不符合Java语法");
                        }
                        sb.append(" is-abstract=\"true\" ");
                        index = Integer.parseInt(split[1]);
                        modifier = getModifier(xml, index);
                        if(modifier==null){
                            //说明不是public类，返回class下标结束方法即可
                            return lastIndex;
                        }
                        split = modifier.split(":");
                        isAbstract = true;
                    }else if(split[0].equals("final")){
                        if(isAbstract){
                            throw new RuntimeException("abstract 和 final 不能共存，不符合Java语法");
                        }
                        sb.append(" is-final=\"true\" ");
                        index = Integer.parseInt(split[1]);
                        modifier = getModifier(xml, index);
                        if(modifier==null){
                            //说明不是public类，返回class下标结束方法即可
                            return lastIndex;
                        }
                        split = modifier.split(":");
                        isFinal = true;
                    }else{
                        throw new RuntimeException("发现未知关键字 ："+split[0]);
                    }
                }
            } else{
                //没有找到修饰符关键字
                return -1;
            }
        }
        //说明该class并非class，而是变量名、方法名等
        return -1;
    }

    /**
     * 回溯找到修饰符，比如public、static等等
     * @param xml
     * @param index
     * @return
     */
    public static String getModifier(String xml, int index ){
        int j = index-1;
        while(j>0&&(xml.charAt(j)==' '||xml.charAt(j)=='\r'||xml.charAt(j)=='\n')){
            j--;
        }
        // j 等于空格前面的修饰符结束字符下标，比如 public class 中的public c下标
        if(j>=0&&j!=index-1){
            //说明前面有空格或者是换行
            int k = j-1;
            while(k>0&&!(xml.charAt(k)==' '||xml.charAt(k)=='\r'||xml.charAt(k)=='\n')){
                k--;
            }
            // k 等于修饰符过后的空格下标，比如 public class 中public p的下标-1
            if(k>=0&&k!=j-1){
                //截取出修饰符
                String sub = xml.substring(k + 1, j+1);
                if(sub.equals("public")||sub.equals("abstract")||sub.equals("final")){
                    return sub+":"+(k + 1);
                }else{
//                    throw new RuntimeException("位置的关键字 : "+sub);
                    return "true";
                }
            }else{
                //没有找到修饰符关键字
                return "true";
            }

        }else{
            //该class并非class，而是变量名、方法名等
            return null;
        }
    }

    public static int indexOf(String xml,int i,String findValue,String errorMessage){
        int lastIndex = indexOf(xml,i,xml.length(),findValue);
        if(lastIndex==-1){
            throw new RuntimeException(errorMessage);
        }
        return lastIndex;
    }

    /**
     * 从xml文本内容，从i下标 开始找到findvalue字符串，返回该字符的下标
     * @param xml
     * @param i
     * @param findValue
     * @return
     */
    public static int indexOf(String xml,int i,int end,String findValue){
        //获取kmp码（下标）
        int[] kmp = nextKMP(findValue);
        int len = 0;
        for(;i<end;i++){
            //找到//的话，直接找到\r\n再继续读取
            if(xml.charAt(i)=='/'&&i+1<xml.length()&&xml.charAt(i+1)=='/'){
                i = find('\r','\n',xml,i);
                if(i==-1){
                    System.out.println("找到//注释，但没有找到结束换行标识，说明//是最后一行");
                    i = xml.length();
                }
            }
            //扎到/*的话，直接找到*/再继续读取
            if(xml.charAt(i)=='/'&&i+1<xml.length()&&xml.charAt(i+1)=='*') {
                i = find('*','/',xml,i);
                if(i==-1){
                    throw new RuntimeException("只找到了/*注释，但没有找到结束标识：*/");
                }
            }
            //kmp算法，避免频繁的回溯
            while(len>0&&xml.charAt(i)!=findValue.charAt(len)){
                len = kmp[len - 1];
            }
            if(xml.charAt(i)==findValue.charAt(len)){
                len++;
            }
            if(len==findValue.length()){
                //找到findValue
                return i - len + 1;
            }

        }
        return -1;
    }

    /**
     * 计算需要查找字符串的KMP下标
     * @param value
     * @return
     */
    public static int[] nextKMP(String value){
        int[] kmp = new int[value.length()];
        int j = 0;
        for(int i = 1;i<value.length();i++){
            while(j>0&&value.charAt(i)!=value.charAt(j)){
                j--;
            }
            if(value.charAt(i)==value.charAt(j)){
                j++;
            }
            kmp[i] = j;
        }
        return kmp;
    }


    /**
     * 找到// 或者 /* 注释
     * @param a
     * @param b
     * @param xml
     * @param i
     * @return
     */
    public static int find(char a,char b,String xml,int i){

        int j = i+2;
        while(j<xml.length()) {
            if (xml.charAt(j) == a && j + 1 < xml.length() && xml.charAt(j + 1) == b) {
                return j + 1;
            }
            j++;
        }
        return -1;
    }

    /**
     * 读取文件内容
     * @param path
     * @param fileName
     * @return
     * @throws IOException
     */
    public static String read(String path , String fileName) throws IOException {
        //读取
        FileReader reader = new FileReader(new File(path,fileName));
        char[] buf = new char[50];
        int len = -1;
        StringBuilder sb = new StringBuilder();
        while((len = reader.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        reader.close();
        return sb.toString();
    }

    /**
     * 写入文件
     * @param path
     * @param fileName
     * @param content
     * @throws IOException
     */
    public static void write(String path, String fileName, String content) throws IOException {
        File filePath = new File(path);
        if(!filePath.exists()){
            filePath.mkdirs();
        }
        File file = new File(path, fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(content);
        writer.flush();
        writer.close();
    }

    public static void show(List list){
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }

}
